package mapspinner.util;

import java.nio.FloatBuffer;

import mapspinner.persistence.datatypes.TerrainSettings;

import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.scene.TriMesh;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.geom.BufferUtils;

public class TerrainTexture4Pass implements TerrainTexture {
	private final TextureState textureState = DisplaySystem.getDisplaySystem()
	.getRenderer().createTextureState();
	private String[] textures;
	private final TriMesh mesh;
	private final TerrainSettings settings;
	
	public TerrainTexture4Pass(TriMesh mesh, TerrainSettings settings) {
		this.settings = settings;
		this.mesh = mesh;		
		mesh.setColorBuffer(BufferUtils.createFloatBuffer(settings.getResolution() * settings.getResolution() * 4));
	}	
	
	public TextureState getTextureState() {
		return textureState;
	}
	
	/**
	 * applies a certain texture layer.
	 * 
	 * @param index
	 */
	public void applyTexture(final int index) {
		if (textures[index] != null)
			textureState.setTexture(
					TextureRegistry.getTexture(textures[index]), index);
		mesh.updateRenderState();
	}

	/**
	 * paints the first two texture layers based on normal angle.
	 * 
	 * @param x
	 *            the x index
	 * @param y
	 *            the y index
	 * @param amount
	 *            the scale of modification
	 */
	public void autoTexture() {
		final FloatBuffer colorBuf = mesh.getColorBuffer();
		final FloatBuffer normBuf = mesh.getNormalBuffer();
		for (int z = 0; z < settings.getResolution(); z++)
			for (int x = 0; x < settings.getResolution(); x++) {
				final Vector3f vec = new Vector3f(normBuf.get((x + z
						* settings.getResolution()) * 3), normBuf.get((x + z
						* settings.getResolution()) * 3 + 1)
						/ settings.getHorizontalScale(), normBuf.get((x + z
						* settings.getResolution()) * 3 + 2)).normalizeLocal();
				colorBuf.put((x + z * settings.getResolution()) * 4, FastMath
						.LERP(FastMath.sin(vec.y), 0.0f, 1.0f));
			}
	}

	/**
	 * @return the texture filenames used in this block
	 */
	public String[] getTextures() {
		return textures;
	}

	/**
	 * paints alpha values onto this terrain block
	 * 
	 * @param x
	 *            the x vertex index to modify
	 * @param z
	 *            the z vertex index to modify
	 * @param amount
	 *            the amount of change
	 * @param texture
	 *            the texture to apply to that layer
	 * @param layer
	 *            the layer to modify
	 */
	public void paint(final int x, final int z, float amount,
			final String texture, int layer) {
		final FloatBuffer colorBuf = mesh.getColorBuffer();
		if (textures[layer] != texture) {
			textures[layer] = texture;
			applyTexture(layer);
		}
		if (layer == 0)
			amount = -amount;
		if (layer > 0)
			--layer;
		float newColor = colorBuf.get((x + z * settings.getResolution()) * 4
				+ layer)
				+ amount / 32;
		if (newColor >= 1)
			newColor = 1;
		if (newColor <= 0)
			newColor = 0;
		colorBuf.put((x + z * settings.getResolution()) * 4 + layer, newColor);
	}

	/**
	 * sets the texture filenames of this block
	 * 
	 * @param textures
	 *            an array of filenames
	 */
	public void setTextures(final String[] textures) {
		if (textures != null)
			this.textures = textures;
		else
			this.textures = new String[] { "darkrock.jpg", "grass.jpg", null,
					null, "noise.gif" };

		for (int x = 0; x < this.textures.length; x++)
			applyTexture(x);
	}

	/**
	 * @return either 3 if four layers are used or 1 if 2 are used this info is
	 *         important for compression purposes.
	 */
	public byte getLayerCount() {
		if (textures[2] != null || textures[3] != null)
			return 3;
		else
			return 1;
	}

}
